#pragma once
#ifndef _KH_CORE_APP_H
#define _KH_CORE_APP_H
#include <Kharlia/Core/Common.h>
#include <Kharlia/Core/ObjectBase.h>
#include <list>

#undef Yield // from WinBase.h

namespace kharlia { namespace core {

class FLogger;
class CSubsystem;
class CTasker;
class CIOManager;

void _InitAppBindings();

struct FSubsystemInfo {
    sint16 Index;
    bool Remove;
    TPtr<CSubsystem> Ptr;
    FSubsystemInfo(
        sint16 index=0,
        TPtr<CSubsystem> p=TPtr<CSubsystem>()
    ): Index(index), Remove(false), Ptr(p) {}
};
    
/// The 'kernel' of the Kharlia application, implements the main loop
/// and handles stackless yielding and sleeping. Also manages subsystems.
class KH_CORE_API CApp: public CObject {
    typedef std::vector<FSubsystemInfo> SubsystemMapType;
    typedef SubsystemMapType::iterator SubsystemMapIter;
    typedef SubsystemMapType::reverse_iterator SubsystemMapRevIter;

    bool mExitRequested;
    bool mExitLoop;
    bool mIsRunning;
    bool mHasRemoves;
    uint16 mThreadSleepTime;
    uint16 mTickRate;
    double mExitWaitTime;
    double mLastTickTime;
    double mTime;
    float mDeltaTime;
    uint32 mTickCount;
    SubsystemMapType mSubsystemsToAdd;
    SubsystemMapType mSubsystems;

    void TickSubsystems(float deltaTime);
    void RunImpl();
    void RequestExitImpl();

    static CApp* msInstance;

    KH_DECL_GC()

public:
    CApp(PyObject* self);
    ~CApp();

    void RequestExit() {
        call_method<void>(mSelf, "RequestExit");
    }
    void ForceExit();
    void Tick();
    // Subsystem management
    void AddSubsystem(sint16, TPtr<CSubsystem>);
    void RemoveSubsystem(TPtr<CSubsystem>);

    KH_DEF_GETSET(uint16, ThreadSleepTime)
    KH_DEF_GETSET(double, ExitWaitTime)
    KH_DEF_GET(float, DeltaTime)
    KH_DEF_GET(double, Time)
    KH_DEF_GET(uint32, TickCount)
    KH_DEF_GET(uint16, TickRate)
    inline void SetTickRate(uint16 value) { mTickRate = value; }

    list GetSubsystemList();

    friend void _InitAppBindings();
    friend TPtr<CApp> GetApp();
    friend CApp* GetAppPtr();
};

inline TPtr<CApp> GetApp() {
    assert(CApp::msInstance);
    return CApp::msInstance->GetSelfPtr<CApp>();
}

inline CApp* GetAppPtr() {
    return CApp::msInstance;
}

/*
inline void SetCurrentContext(str ctxt) {
    GetCurrentTasklet().attr("context") = ctxt;
}
inline void SetCurrentContext() {
    GetCurrentTasklet().attr("context") = object();
}
*/

inline uint32 GetRealTickCount() {
#if KH_PLATF_WIN
    return ::clock(); 
#elif KH_PLATF_LINUX
#error
#endif
}

#define KH_TICKS_PER_SECOND CLOCKS_PER_SEC

inline double GetRealTime() {
    return double(GetRealTickCount()) / KH_TICKS_PER_SECOND;
}

inline void SleepThread(uint32 miliseconds) {
#if KH_PLATF_WIN
    ::Sleep(miliseconds);
#elif KH_PLATF_LINUX
    ::usleep(miliseconds * 1000);
#endif
}

}} // namespace Kharlia

ENABLE_BACK_REFERENCE(kharlia::core::CApp)

#endif